/** @file    UserManager.cpp
 *  @author  Alessandro Polo
 *  @version $Id: UserManager.cpp 2832 2010-08-02 22:53:46Z alex $
 *  @brief
 * File containing methods for the wosh::UserManager class.
 * The header for this class can be found in UserManager.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/
 
 #include <core/UserManager.h>
 #include <core/ObjectAllocator.h>
 #include <core/WoshKernel.h>
 #include <core/FileSystem.h>
 #include <core/PersistenceManager.h>
 #include <core/Settings.h>
 #include <core/Thread.h>
 #include <core/SystemError.h>
 #include <core/MethodsCommon.h>
 #include <core/MessageProcessingCommon.h>
 #include <core/MessageFilter.h>
 #include <core/Fact.h>
 #include <core/List.h>
 #include <core/Table.h>
 #include <core/Functional.h>
 #include <core/Utilities.h>
 #include <core/DataModels.h>

 #include <interfaces/services/CommunicatorService.h>
 #include <framework/communication/CommunicationEvents.h> // BAD BUG TODO


using namespace std;
namespace wosh {

// Gamma singleton! static reference is allocated and maintained by WoshKernel
extern "C" {
	UserManager* UserManager::getInstance() { 
		if ( WoshKernel::getInstance() == NULL ) return NULL;
		return &WoshKernel::getInstance()->users();
	}
}
 // register as abstract because it is a singleton:
 WOSH_REGISTER_INTERFACE(wosh::UserManager, "wosh::WoshModule", 0.01, _static_UserManager )

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

UserManager::UserManager( const WoshKernel* parent )
	: WoshModule(parent, _UserManager_NAME, _UserManager_URI) {

	GroupsVirtualURI.setParent( Object::getURI() );
	GroupsVirtualURI.setName( "Groups" );
	GroupsVirtualURI.registerGlobal();

//Log.setLevel( VERBOSE );

	Log(LOG_DEBUG, " Creating default User(s) and UserGroup(s).." );
	createDefaultUsersGroups();

	Log(LOG_DEBUG, " Initializing Properties.." );
	Properties.set( _UserManager_KEY_Users, this->users.count(), Permission(Permission::Read) );
	Properties.set( _UserManager_KEY_UsersDB, "$DATABASE/users/users.xml", Permission::RW_R__R__() );
	Properties.set( _UserManager_KEY_Groups, this->groups.count(), Permission(Permission::Read) );
	Properties.set( _UserManager_KEY_GroupsDB, "$DATABASE/users/groups.xml", Permission::RW_R__R__() );

	Log(LOG_DEBUG, " Registering Methods.." );
	MethodRequest* mmGetUsersLocations = Methods.create<MethodRequest>( _UserManager_METHOD_listloc, "Get Users' Locations", Permission::R_XR_XR__() );
	mmGetUsersLocations->setMethod( this, (MethodRequestPtr)&UserManager::mmDoGetUsersLocations );

	MethodRequest* mmGetUsersCommunicators = Methods.create<MethodRequest>( _UserManager_METHOD_listcomm, "Get Users' Communicators", Permission::R_XR_XR__() );
	mmGetUsersCommunicators->setMethod( this, (MethodRequestPtr)&UserManager::mmDoGetUsersCommunicators );

	MethodMessageResponse* mmForceUserIn = Methods.create<MethodMessageResponse>( _UserManager_METHOD_forcelocation, "force user in a location", Permission::R_XR__R__() );
	mmForceUserIn->setMethod( this, (MethodMessageResponsePtr)&UserManager::mmDoForceUserIn );

	MethodMessageResponse* mmSearchUser = Methods.create<MethodMessageResponse>( _UserManager_METHOD_search, "search user matching pattners", Permission(Permission::RX) );
	mmSearchUser->setMethod( this, (MethodMessageResponsePtr)&UserManager::mmDoSearchUser );

	MethodList* mmListUsers = Methods.create<MethodList>( _METHOD_List, "List Users", Permission::R_XR_XR__() );
	mmListUsers->setDataFieldExtractor( DataFieldExtractor<User>::createFor< DataAdapterMapT<string, User> >(this->users, "Users") );

	MethodList* mmListUsersGroups = Methods.create<MethodList>( _UserManager_METHOD_listgroups, "List Groups", Permission::R_XR_XR__() );
	mmListUsersGroups->setDataFieldExtractor( DataFieldExtractor<UserGroup>::createFor< DataAdapterMapT<string, UserGroup> >(this->groups, "Groups") );
 }

UserManager::~UserManager() {
	Log(LOG_DEBUG, ":~UserManager() Destroying.." );

	Log(LOG_VERBOSE, ":~UserManager() Freeing Users [%d]..", this->users.count() );
	this->users.clear();

	Log(LOG_VERBOSE, ":~UserManager() Freeing Groups [%d]..", this->groups.count() );
	this->groups.clear();

	Log(LOG_VERBOSE, " Destroyed." );
 }

//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////// KERNEL EVENTS

WRESULT UserManager::onKernelInitializing() {
	WRESULT ret = WoshModule::onKernelInitializing();

	BusSecurity.setMessageHandler(this);
	WRESULT bus_d_connected = BusSecurity.connect( _Bus_Security );
	if ( WFAILED(bus_d_connected) )
		SystemError::raise(this, Log, SystemError::CRITICAL, 0, "init", "FAILED#%d Connecting Bus ("_Bus_Security")..", bus_d_connected );
	else
		Log(LOG_INFO, " Connected to "_Bus_Security"." );

	ret += UserManager::loadUsers();
	//ret += UserManager::loadGroups();


PersistenceDbGeneric<User> db;
db.getURI().setParent(this->getURI());
db.setName("UsersDB");
db.setLog( Log );
db.setAdapter( new DataAdapterMapT<string, User>(this->users) );
db.setProtocol("XML");
db.setFileInput( "$DATABASE/users/users.xml" );
db.setFileOutput( "$DATABASE/users/users.xml.bak" );
db.save();

	return ret;
 }

WRESULT UserManager::onKernelStopping() {
	WRESULT ret = WRET_OK;
	Log(LOG_VERBOSE, ":onKernelStopping() Saving Users and Groups DB.." );
	this->users.transactionBeginRead();
	ret += PersistenceManager::saveObjects( this->users.getMap(), "XML", "$DATABASE/users/users.xml.bak" );
	this->users.transactionEnd();
	if ( WFAILED(ret) )
		Log(LOG_CRITICAL, ":onKernelStopping() ERROR#%d Saving Users and Groups DB.", ret );
	ret += WoshModule::onKernelStopping();
	return ret;
 }

/////////////////////////////////////////////////////////////////////////////////////////////// KERNEL EVENTS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////// PROCESS BUS MESSAGES

void UserManager::busMessage( const Message& message, const Bus* source ) {
	if ( message.isEmpty() ) return;
	// avoid message replicas
	if ( !MessageFilter::filterReplica( message, &BusCore ) )
		return;

	if ( message.getContent()->isFact() ) {
		if ( message.getContent()->isKindOf<communication::UserPresence_StateChanged>() ) {
			const communication::UserPresence_StateChanged* upEvent = message.getContent()->as<communication::UserPresence_StateChanged>();
			this->users.transactionBeginWrite();
			User* cUser = this->users.find( upEvent->getUserName() );
			if ( cUser == NULL ) {
				this->users.transactionEnd();
				return;
			 }
			int64 timestamp = message.getTimestamp();
			if ( upEvent->isUserOnline() ) {
				cUser->updateCommmunicator(message.getSource().toString(), timestamp);
			 }
			else {
				cUser->updateCommmunicator(message.getSource().toString(), -timestamp);
			 }
			this->users.transactionEnd();
		 }
		else if ( message.getContent()->isKindOf<communication::UserInteraction_Event>() ) {
			const communication::UserInteraction_Event* upEvent = message.getContent()->as<communication::UserInteraction_Event>();
			this->users.transactionBeginWrite();
			User* cUser = this->users.find( upEvent->getUserName() );
			if ( cUser == NULL ) {
				this->users.transactionEnd();
				return;
			 }
			int64 timestamp = message.getTimestamp();
			cUser->updateCommmunicator(message.getSource().toString(), timestamp);
			this->users.transactionEnd();
		 }
		return;
	 }

	if ( !message.getContent()->isRequest() ) return;

	if ( !message.getDestination().isChildOf(this->getURI(), true) ) {
		WoshModule::busMessage(message, source);
		return;
	 }

	Log(LOG_VERBOSE, ":busMessage() = Message-Request for Child '%s'", message.getDestination().getName().c_str() );
	this->users.transactionBeginRead(); // following operation won't ever really add/delete a channel item
	MPC_mmDo_InContainer<User>(message, this->users, Log, BusCore);
	this->users.transactionEnd();
 }

//////////////////////////////////////////////////////////////////////////////////////// PROCESS BUS MESSAGES
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////// COLLECTOR METHODS

WRESULT UserManager::registerUser( User* user ) {
	if ( user == NULL ) return WRET_ERR_PARAM;
	MutexLockerWrite mL(this->users.getMutex());
	return registerUser_( user, true );
 }

WRESULT UserManager::registerGroup( UserGroup* usergroup ) {
	if ( usergroup == NULL ) return WRET_ERR_PARAM;
	MutexLockerWrite mL(this->groups.getMutex());
	return registerGroup_( usergroup, true );
 }

WRESULT UserManager::registerUser_( User* user, bool raise_event ) {
	if ( user == NULL ) return WRET_ERR_PARAM;
	if ( this->users.existsValue(user) ) {
		Log(LOG_CRITICAL, ":registerUser_(%s) Instance already registered! [ID#%ld]", user->getName().c_str(), user->getID() );
		return WRET_ERR_PARAM;
	 }
	if ( user->getURI().isGlobal() ) user->getURI().unRegisterGlobal();
	user->getURI().setParent( this->getURI() );
	user->getURI().setName( user->getName() );
	user->getURI().registerGlobal();
	user->setPermission( Permission::RWXR_____(user->getName()) );
	this->groups.transactionBeginWrite();
	tStrUserGroupMap::ConstIterator it_g;
	const tStrUserGroupMap::ConstIterator it_g_end = this->groups.end();
	std::vector<std::string>::iterator it;
	bool editing = true;
	while (editing) {
		editing = false;
		for ( it=user->getGroups().begin(); it!=user->getGroups().end(); ++it ) {
			bool found = false;
			for ( it_g=this->groups.begin(); it_g!=it_g_end; ++it_g ) {
				if ( it_g->second == NULL ) continue;
				if ( it_g->second->getName() != *it ) continue;
				it_g->second->setUserCount( it_g->second->getUserCount() + 1 );
				found = true;
				break;
			}
			if ( !found ) {
				Log(LOG_WARNING, ":registerUser_(%s) Group %s NOT FOUND!", user->getName().c_str(), (*it).c_str() );
				user->getGroups().erase(it);
				editing = true;
				break;
			 }
		 }
	 }
	this->groups.transactionEnd();
	User* oldObj = this->users.find(user->getName());
	Fact* fact = NULL;
	if ( raise_event ) {
		if ( oldObj == NULL )
			fact = new UserManager_UserListChanged(user->getName(), UserManager_UserListChanged::EVENT_LISTITEM_ADDED );
		else
			fact = new UserManager_UserListChanged(user->getName(), UserManager_UserListChanged::EVENT_LISTITEM_UPDATED );
	 }
	if ( oldObj == NULL ) {
		Log(LOG_INFO, ":registerUser_(%s) Registered New ID#%ld", user->getName().c_str(), user->getID() );
		this->users.set(user->getName(), user);
	 }
	else {
		Log(LOG_INFO, ":registerUser_(%s) User Exists, merging Properties [ID#%ld]", user->getName().c_str(), user->getID() );
		// user already registered, 
		///@todo should merge info!
		oldObj->Properties.intersect(user->Properties, true, false);
		delete user;
	 }
	Properties.transactionBeginWrite();
	Properties.update( _UserManager_KEY_Users, this->users.size() );
	Properties.transactionEnd();
	if ( fact != NULL ) {
		Message* msg_event = new Message(fact);
		msg_event->setDestinationBroadcast(true);
		signMessage(msg_event);
		BusSecurity.postMessage(msg_event);
	 }
	return WRET_OK;
 }

WRESULT UserManager::registerGroup_( UserGroup* usergroup, bool raise_event ) {
	if ( usergroup == NULL ) return WRET_ERR_PARAM;
	if ( this->groups.existsValue(usergroup) ) {
		Log(LOG_CRITICAL, ":registerGroup_(%s) Instance already registered! [ID#%ld]", usergroup->getName().c_str(), usergroup->getID() );
		return WRET_ERR_PARAM;
	 }
	if ( usergroup->getName().size() < 3 ) return WRET_ERR_PARAM;
	usergroup->getURI().clear();
	usergroup->getURI().setParent( this->GroupsVirtualURI );
	usergroup->getURI().setName( usergroup->getName() );
	usergroup->getURI().registerGlobal();
	usergroup->setPermission( Permission::RWXRWX___("root", usergroup->getName()) );
	UserGroup* oldObj = this->groups.find(usergroup->getName());
	Fact* fact = NULL;
	if ( raise_event ) {
		if ( oldObj == NULL )
			fact = new UserManager_GroupListChanged(usergroup->getName(), UserManager_GroupListChanged::EVENT_LISTITEM_ADDED );
		else
			fact = new UserManager_GroupListChanged(usergroup->getName(), UserManager_GroupListChanged::EVENT_LISTITEM_UPDATED );
	 }
	if ( oldObj == NULL ) {
		Log(LOG_INFO, ":registerGroup_(%s) Registered ID#%ld", usergroup->getName().c_str(), usergroup->getID() );
		this->groups.set(usergroup->getName(), usergroup);
	 }
	else {
		Log(LOG_WARNING, ":registerGroup_(%s) Group Exists, merging Properties [ID#%ld]", usergroup->getName().c_str(), usergroup->getID() );
		// user already registered, 
		///@todo should merge info!
		delete usergroup;
	 }
	Properties.transactionBeginWrite();
	Properties.update( _UserManager_KEY_Groups, this->groups.size() );
	Properties.transactionEnd();
	if ( fact != NULL ) {
		Message* msg_event = new Message(fact);
		msg_event->setDestinationBroadcast(true);
		signMessage(msg_event);
		BusSecurity.postMessage(msg_event);
	 }
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void UserManager::createDefaultUsersGroups() {
	Log(LOG_VERBOSE, ":createDefaultUsersGroups() : Creating 5 Groups.." );
	registerGroup_( new UserGroup(1,	"root"), false );
	registerGroup_( new UserGroup(10,	"virtual"), false );
	registerGroup_( new UserGroup(20,	"nobody"), false );
	registerGroup_( new UserGroup(110,	"inhabitants"), false );
	registerGroup_( new UserGroup(3200,	"friends"), false );

	Log(LOG_VERBOSE, ":createDefaultUsersGroups() : Creating 4 Users.." );
	registerUser_( new User(1,	"root",		"wosh",	"root;virtual"), false );
	registerUser_( new User(10,	"wosh",		"",		"root;virtual"), false );
	registerUser_( new User(50,	"nobody",	"",		"nobody;virtual"), false );
	registerUser_( new User(52,	"anonymous","",		"nobody;virtual"), false );
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT UserManager::loadGroups() {
	// may also be a folder of .xml files
	string xmlfile = Properties.getValueOf(_UserManager_KEY_GroupsDB).toString();
	Log(LOG_VERBOSE, ":loadGroups() File %s ..", xmlfile.c_str() );
	std::vector<UserGroup*> objects_load;
	WRESULT ret = PersistenceManager::loadObjects( objects_load, "XML", xmlfile );
	if ( WFAILED(ret) ) {
		Log(LOG_CRITICAL, ":loadGroups() FAILED#%ld loading objects!", ret );
		return ret;
	 }
	this->groups.transactionBeginWrite();
	std::vector<UserGroup*>::iterator it_load;
	for ( it_load=objects_load.begin(); it_load!=objects_load.end(); ++it_load ) {
		UserGroup* currObj = *it_load;
		if ( currObj == NULL ) continue;
		ret += registerGroup_( currObj, true );
	 }
	this->groups.transactionEnd();
	Log(LOG_VERBOSE, ":loadGroups() Loaded %d groups", this->groups.count() );
	return ret;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT UserManager::loadUsers() {
	// may also be a folder of .xml files
	string xmlfile = Properties.getValueOf(_UserManager_KEY_UsersDB).toString();
	Log(LOG_VERBOSE, ":loadUsers() File %s ..", xmlfile.c_str() );
	std::vector<User*> objects_load;
	WRESULT ret = PersistenceManager::loadObjects( objects_load, "XML", xmlfile );
//	WRESULT ret = DataBaseManager::loadObjects( objects_load, "wosh::User", xmlfile, "xml" );
	if ( WFAILED(ret) ) {
		Log(LOG_CRITICAL, ":loadUsers() FAILED#%d loading objects!", ret );
		return ret;
	 }
	this->users.transactionBeginWrite();
	std::vector<User*>::iterator it_load;
	for ( it_load=objects_load.begin(); it_load!=objects_load.end(); ++it_load ) {
		if ( *it_load == NULL ) continue;
		if ( (*it_load)->getName().size() < 3 ) {
			Log(LOG_WARNING, ":loadUsers() User '%s' invalid name", (*it_load)->getName().c_str() );
			delete *it_load;
			continue;
		 }
		ret += registerUser_( *it_load, true );
	 }
	this->users.transactionEnd();
	Log(LOG_VERBOSE, ":loadUsers() Loaded %d users", this->users.size() );
	return ret;
 }

/////////////////////////////////////////////////////////////////////////////////////////// COLLECTOR METHODS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////// METHODS

Response* UserManager::mmDoSearchUser( const Message* requestMessage ) {
	if ( requestMessage == NULL || requestMessage->isEmpty() ) return NULL;
	if ( !requestMessage->getContent()->isRequest() ) return NULL;
	const Request* request = requestMessage->getContent()->asRequest();
	if ( !request->hasData() ) {
		return request->replyResponse(WRET_ERR_PARAM, "Invalid data");
	 }
	string key;
	Variant propValue;
	if ( request->getData()->isKindOf<Property>() ) {
		const Property* prop = request->getData()->as<Property>();
		if ( prop == NULL )
			return request->replyResponse(WRET_ERR_PARAM, "Invalid data");
		key = prop->getKey();
		propValue = prop->getValue();
	 }
	std::vector<std::string> fusers = findUserByProperty( key, propValue, requestMessage->getSecurityToken() );
	Table* tbl = new Table(fusers.size(), 1, true);
	tbl->setTableName("Users matching -");
	tbl->setHeaderHorizontal("Name", 0);
	std::vector<std::string>::iterator it;
	unsigned int iRow = 0;
	for ( it=fusers.begin(); it!=fusers.end(); ++it, ++iRow ) {
		tbl->set<Variant>(*it, iRow, 0);
	 }
	return request->replyResponse(WRET_OK, tbl);
/*
	List* list = new List(fusers.size());
	std::vector<std::string>::iterator it;
	for ( it=fusers.begin(); it!=fusers.end(); ++it ) {
		list->push_back<Variant>(*it);
	 }
	return request->replyResponse(WRET_OK, list);
*/
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* UserManager::mmDoGetUsersCommunicators( const Request* request ) {
	if ( request == NULL ) return NULL;
	string option = request->getArgument().toString();
	Log(LOG_VERBOSE, ":mmDoGetUsersCommunicators(%s)", option.c_str() );
	if ( option == "s" || option == "short" ) {
		this->users.transactionBeginRead();
		List* objs = new List(this->users.size());
		tStrUserMap::ConstIterator it;
		const tStrUserMap::ConstIterator it_end = this->users.end();
		for ( it=this->users.begin(); it!=it_end; ++it ) {
			if ( it->second == NULL ) continue;
			objs->push_back<Variant>( it->second->getCommunicators().getLastName() );
		 }
		this->users.transactionEnd();
		return request->replyResponse(WRET_OK, objs);
	 }
	else {
		this->users.transactionBeginRead();
		Table* tbl = new Table( this->users.size(), 4, true );
		tbl->setTableName("Users' Communicators");
		tbl->setHeaderHorizontal("Username", 0);
		tbl->setHeaderHorizontal("LastCommunicator", 1);
		tbl->setHeaderHorizontal("TimeStamp", 2);
		tbl->setHeaderHorizontal("Ranking", 3);
		unsigned int iRow = 0;
		tStrUserMap::ConstIterator it;
		const tStrUserMap::ConstIterator it_end = this->users.end();
		for ( it=this->users.begin(); it!=it_end; ++it, ++iRow ) {
			if ( it->second == NULL ) continue;
			tbl->set( new Variant(it->second->getName()), iRow, 0);
			string commURI;
			UserInfoCommmunicatorData data;
			it->second->getCommunicators().getCommmunicatorLast(commURI, data);
			tbl->set( new Variant(commURI), iRow, 1);
			tbl->set( new Variant(data.timestamp), iRow, 2);
			tbl->set( new Variant(data.ranking), iRow, 3);
		 }
		this->users.transactionEnd();
		return request->replyResponse(WRET_OK, tbl);
	 }
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* UserManager::mmDoGetUsersLocations( const Request* request ) {
	if ( request == NULL ) return NULL;
	string option = request->getArgument().toString();
	Log(LOG_VERBOSE, ":mmDoGetUsersLocations(%s)", option.c_str() );
	if ( option == "s" || option == "short" ) {
		this->users.transactionBeginRead();
		List* objs = new List(this->users.size());
		tStrUserMap::ConstIterator it;
		const tStrUserMap::ConstIterator it_end = this->users.end();
		for ( it=this->users.begin(); it!=it_end; ++it ) {
			if ( it->second == NULL ) continue;
			objs->push_back<Variant>( it->second->getLocation().getLastName() );
		 }
		this->users.transactionEnd();
		return request->replyResponse(WRET_OK, objs);
	 }
	else {
		this->users.transactionBeginRead();
		Table* tbl = new Table( this->users.size(), 5, true );
		tbl->setTableName("Users' Locations");
		tbl->setHeaderHorizontal( "Name", 0);
		tbl->setHeaderHorizontal( "AtHome", 1);
		tbl->setHeaderHorizontal( "LastCommunicator", 2);
		tbl->setHeaderHorizontal( "LastCommTs", 3);
		tbl->setHeaderHorizontal( "LastCommProb", 4);
		unsigned int iRow = 0;
		tStrUserMap::ConstIterator it;
		const tStrUserMap::ConstIterator it_end = this->users.end();
		for ( it=this->users.begin(); it!=it_end; ++it, ++iRow ) {
			if ( it->second == NULL ) continue;
			tbl->set<Variant>( it->second->getName(), iRow, 0);
			tbl->set<Variant>( it->second->isAtHome(), iRow, 1);
			string locURI;
			UserInfoLocationData data;
			it->second->getLocation().getLocationLast(locURI, data);
			tbl->set<Variant>( locURI, iRow, 2);
			tbl->set( new Variant(data.timestamp, Variant::DATETIME), iRow, 3);
			tbl->set<Variant>( data.probability, iRow, 4);
		 }
		this->users.transactionEnd();
		return request->replyResponse(WRET_OK, tbl);
	 }
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Response* UserManager::mmDoForceUserIn( const Message* requestMessage ) {
	if ( requestMessage == NULL || requestMessage->isEmpty() ) return NULL;
	if ( !requestMessage->getContent()->isRequest() ) return NULL;
	const Request* request = requestMessage->getContent()->asRequest();
	if ( !request->hasData() )
		return request->replyResponse(WRET_ERR_PARAM, "Invalid data");
	Variant dUsername; Variant dLocation;
	request->extractArguments(dUsername, dLocation);
	if ( !dUsername.isStringNotEmpty() || !dLocation.isStringNotEmpty() ) {
		return request->replyResponse(WRET_ERR_PARAM, "Invalid arguments [username, location]");
	 }
	std::string sender = "";
	if ( requestMessage->getSecurityToken() != NULL )
		sender = requestMessage->getSecurityToken()->getUsername();
	Log(LOG_INFO, "mmDoForceUserIn() : Force %s in %s", dUsername.asString().c_str(), dLocation.asString().c_str() );
	this->users.transactionBeginWrite();
	User* oldObj = this->users.find(dUsername.asString());
	if ( oldObj == NULL ) {
		this->users.transactionEnd();
		return request->replyResponse(WRET_ERR_PARAM, "Invalid username");
	 }
	oldObj->updateLocation(dLocation.asString(), Utilities::std_time(), 1.0);
	this->users.transactionEnd();
	return request->replyResponse(WRET_OK);
 }

///////////////////////////////////////////////////////////////////////////////////////////////////// METHODS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////// STATIC METHODS

bool UserManager::isUser( const std::string& user ) {
	UserManager* usrman = UserManager::getInstance();
	if ( usrman == NULL ) return false;

	MutexLockerRead mL(usrman->users.getMutex());
	User* oldObj = usrman->users.find(user);
	return ( oldObj != NULL );
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT UserManager::isAuthorized( const std::string& wosh_username, const SecurityTokenProvider* credentials ) {
	if ( credentials == NULL ) return WRET_ERR_PARAM;
	UserManager* usrman = UserManager::getInstance();
	if ( usrman == NULL ) return WRET_ERR_INTERNAL;
	//ThreadImpl::sleepForMSec( Utilities::randomInt() % 100 );

	MutexLockerRead mL(usrman->users.getMutex());
	User* oldObj = usrman->users.find(wosh_username);
	if ( oldObj == NULL )
		return WRET_ERR_PARAM;
	return WRET_OK;//credentials->validate(oldObj);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool UserManager::isGroupOfUser( const std::string& user, const std::string& group ) {
	UserManager* usrman = UserManager::getInstance();
	if ( usrman == NULL ) return false;

	MutexLockerRead mL(usrman->users.getMutex());
	User* oldObj = usrman->users.find(user);
	if ( oldObj == NULL )
		return false;
	return oldObj->isMemberOf(group);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Variant UserManager::getUserProperty( const std::string& user, const std::string& key, const SecurityToken* )
 {
	UserManager* usrman = UserManager::getInstance();
	if ( usrman == NULL ) return Variant();

	MutexLockerRead mL(usrman->users.getMutex());
	User* oldObj = usrman->users.find(user);
	if ( oldObj == NULL )
		return Variant();
	return oldObj->getValueOf(key);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

std::string UserManager::getUserLocation( const std::string& user, const SecurityToken* queryOwner )
 {
	return UserManager::getUserProperty(user, "LocationLast", queryOwner).toString();
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

UserInfoCommmunicators* UserManager::getUserCommunicators( const std::string& user, const SecurityToken* )
 {
	UserManager* usrman = UserManager::getInstance();
	if ( usrman == NULL ) return NULL;
	MutexLockerRead mL(usrman->users.getMutex());
	User* cUser = usrman->users.find(user);
	if ( cUser == NULL )
		return NULL;
	return cUser->getCommunicators().clone();
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

int UserManager::getUserCountAtHome()
 {
	UserManager* usrman = UserManager::getInstance();
	if ( usrman == NULL ) return -1;

	int count = 0;
	MutexLockerRead mL(usrman->users.getMutex());
	tStrUserMap::ConstIterator it;
	const tStrUserMap::ConstIterator it_end = usrman->users.end();
	for ( it=usrman->users.begin(); it!=it_end; ++it ) {
		if ( it->second == NULL ) continue;
		if ( it->second->isAtHome() )
			++count;
	 }
	return count;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT UserManager::setUserProperty( const std::string& user, const std::string& key, const Variant& data, const SecurityToken* )
 {
	UserManager* usrman = UserManager::getInstance();
	if ( usrman == NULL ) return WRET_ERR_ILLEGAL_USE;

	MutexLockerRead mL(usrman->users.getMutex());
	User* oldObj = usrman->users.find(user);
	if ( oldObj == NULL )
		return WRET_ERR_PARAM;
	return oldObj->setProperty(key, data);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT UserManager::setUserLocation( const std::string& user, const string& new_location, double probability, short at_home )
 {
	UserManager* usrman = UserManager::getInstance();
	if ( usrman == NULL ) return WRET_ERR_ILLEGAL_USE;

	usrman->users.transactionBeginRead();
	User* oldObj = usrman->users.find(user);
	if ( oldObj == NULL ) {
		usrman->users.transactionEnd();
		return WRET_ERR_PARAM;
	 }
	oldObj->updateLocation(new_location, Utilities::std_time(), probability);
	if ( at_home != 2 )
		oldObj->setAtHome(at_home);

	int usersAtHome = 0;
	tStrUserMap::ConstIterator it;
	const tStrUserMap::ConstIterator it_end = usrman->users.end();
	for ( it=usrman->users.begin(); it!=it_end; ++it ) {
		if ( it->second == NULL ) continue;
		if ( it->second->isAtHome() )
			++usersAtHome;
	 }

	usrman->users.transactionEnd();

	List* dL = new List();
	dL->push_back<Variant>( user );
	dL->push_back<Variant>( new_location );
	dL->push_back<Variant>( probability );
	dL->push_back<Variant>( at_home );
	Message* msg_event = new Message( new Fact( _UserManager_EVENT_located, dL) );
	msg_event->setSource( usrman );
	msg_event->setDestinationBroadcast();
	msg_event->setDestinationBus(_Bus_Security);
	usrman->BusSecurity.postMessage(msg_event);

	if ( usersAtHome == 0 ) {
		Message* msg_event = new Message( new Fact( _UserManager_EVENT_UsersAtHome, new Variant(usersAtHome)) );
		msg_event->setSource( usrman );
		msg_event->setDestinationBroadcast();
		msg_event->setDestinationBus(_Bus_Security);
		usrman->BusSecurity.postMessage(msg_event);
	 }
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT UserManager::setUserCommunicator( const std::string& user, const string& communicator, long timestamp, double ranking_offset )
 {
	UserManager* usrman = UserManager::getInstance();
	if ( usrman == NULL ) return WRET_ERR_ILLEGAL_USE;

	MutexLockerRead mL(usrman->users.getMutex());
	User* oldObj = usrman->users.find(user);
	if ( oldObj == NULL )
		return WRET_ERR_PARAM;
	oldObj->updateCommmunicator(communicator, timestamp, ranking_offset);
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

std::vector<std::string> UserManager::findUserByProperty( const string& key, const Variant& value, const SecurityToken* )
 {
	std::vector<std::string> res;
	UserManager* usrman = UserManager::getInstance();
	if ( usrman == NULL ) return res;

	MutexLockerRead mL(usrman->users.getMutex());
	tStrUserMap::ConstIterator it;
	const tStrUserMap::ConstIterator it_end = usrman->users.end();
	for ( it=usrman->users.begin(); it!=it_end; ++it ) {
		if ( it->second == NULL ) continue;
		if ( !it->second->getProperties().exists(key) ) continue;
		if ( !value.isEmpty() && it->second->getValueOf(key) != value ) continue;
		res.push_back( it->second->getName() );
	 }
	return res;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

 WOSH_REGISTER(wosh::UserManager_UserListChanged, "wosh::Fact", 1.0, _static_UserManager_UserListChanged )

std::string UserManager_UserListChanged::getUserName() const {
	const ISerializable* item = getItem();
	if ( item != NULL && item->isKindOf<wosh::User>() ) {
		return item->as<wosh::User>()->getName();
	 }
	const Variant* var = getItemKey();
	if ( var == NULL ) return "";
	return var->toString();
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

 WOSH_REGISTER(wosh::UserManager_GroupListChanged, "wosh::Fact", 1.0, _static_UserManager_GroupListChanged )

std::string UserManager_GroupListChanged::getGroupName() const {
	const ISerializable* item = getItem();
	if ( item != NULL && item->isKindOf<wosh::UserGroup>() ) {
		return item->as<wosh::UserGroup>()->getName();
	 }
	const Variant* var = getItemKey();
	if ( var == NULL ) return "";
	return var->toString();
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////


}; // namespace wosh
